﻿#region File and License Information
/*
<File>
	<Copyright>Copyright © 2010, Daniel Vaughan. All rights reserved.</Copyright>
	<License>
		Redistribution and use in source and binary forms, with or without
		modification, are permitted provided that the following conditions are met:
			* Redistributions of source code must retain the above copyright
			  notice, this list of conditions and the following disclaimer.
			* Redistributions in binary form must reproduce the above copyright
			  notice, this list of conditions and the following disclaimer in the
			  documentation and/or other materials provided with the distribution.
			* Neither the name of the <organization> nor the
			  names of its contributors may be used to endorse or promote products
			  derived from this software without specific prior written permission.

		THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
		EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
		WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
		DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
		DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
		(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
		LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
		ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
		(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
		SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	</License>
	<Owner Name="Daniel Vaughan" Email="dbvaughan@gmail.com"/>
	<CreationDate>2010-03-03 00:59:46Z</CreationDate>
</File>
*/
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;

using DanielVaughan.Calcium.Content;
using DanielVaughan.Calcium.Gui.Controls;
using DanielVaughan.Calcium.Gui.Windows;
using DanielVaughan.Calcium.Modularity;
using DanielVaughan.Calcium.Services;
using DanielVaughan.Gui;
using DanielVaughan.IO;
using DanielVaughan.Windows;

using Microsoft.Practices.Composite;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Presentation.Regions;
using Microsoft.Practices.Composite.Regions;

namespace DanielVaughan.Calcium.Gui
{
	[TemplatePart(Name = PART_Grid_Root,					Type = typeof(Grid))]
	[TemplatePart(Name = PART_ColumnDefinition_Left,		Type = typeof(ColumnDefinition))]
	[TemplatePart(Name = PART_ColumnDefinition_Center,		Type = typeof(ColumnDefinition))]
	[TemplatePart(Name = PART_ColumnDefinition_Right,		Type = typeof(ColumnDefinition))]
	[TemplatePart(Name = PART_RowDefinition_Banner,			Type = typeof(RowDefinition))]
	[TemplatePart(Name = PART_RowDefinition_Menu,			Type = typeof(RowDefinition))]
	[TemplatePart(Name = PART_RowDefinition_Content,		Type = typeof(RowDefinition))]
	[TemplatePart(Name = PART_RowDefinition_OutputDisplay,	Type = typeof(RowDefinition))]
	[TemplatePart(Name = PART_RowDefinition_StatusBar,		Type = typeof(RowDefinition))]
	[TemplatePart(Name = PART_Border_Banner,				Type = typeof(Border))]
	[TemplatePart(Name = PART_Expander_Banner,				Type = typeof(Expander))]
	[TemplatePart(Name = PART_StackPanel_TitleBanner,		Type = typeof(StackPanel))]
	[TemplatePart(Name = PART_ContentControl_Menu,			Type = typeof(ContentControl))]
	[TemplatePart(Name = PART_StackPanel_Menu,				Type = typeof(StackPanel))]
	[TemplatePart(Name = PART_Border_Workspace,				Type = typeof(Border))]
	[TemplatePart(Name = PART_TabControl_Workspace,			Type = typeof(TabControl))]
	[TemplatePart(Name = PART_Border_Left,					Type = typeof(Border))]
	[TemplatePart(Name = PART_Expander_Left,				Type = typeof(Expander))]
	[TemplatePart(Name = PART_TabControl_Left,				Type = typeof(TabControl))]
	[TemplatePart(Name = PART_Border_Right,					Type = typeof(Border))]
	[TemplatePart(Name = PART_Expander_Right,				Type = typeof(Expander))]
	[TemplatePart(Name = PART_TabControl_Right,				Type = typeof(TabControl))]
	[TemplatePart(Name = PART_Border_Bottom,				Type = typeof(Border))]
	[TemplatePart(Name = PART_Expander_Bottom,				Type = typeof(Expander))]
	[TemplatePart(Name = PART_TabControl_Bottom,			Type = typeof(TabControl))]
	[TemplatePart(Name = PART_StackPanel_StatusBar,			Type = typeof(StackPanel))]
	[TemplatePart(Name = PART_GridSplitter_Left,			Type = typeof(GridSplitter))]
	[TemplatePart(Name = PART_GridSplitter_Bottom,			Type = typeof(GridSplitter))]
	[TemplatePart(Name = PART_GridSplitter_Right,			Type = typeof(GridSplitter))]
	public partial class ShellView : Window, IShellView, IMainWindow
	{
		public const string PART_Grid_Root						= "PART_Grid_Root";
		Grid grid_Root;
		public const string PART_ColumnDefinition_Left			= "PART_ColumnDefinition_Left";
		ColumnDefinition columnDefinition_Left;
		public const string PART_ColumnDefinition_Center		= "PART_ColumnDefinition_Center";
		ColumnDefinition columnDefinition_Center;
		public const string PART_ColumnDefinition_Right			= "PART_ColumnDefinition_Right";
		ColumnDefinition columnDefinition_Right;
		public const string PART_RowDefinition_Banner			= "PART_RowDefinition_Banner";
		RowDefinition rowDefinition_Banner;
		public const string PART_RowDefinition_Menu				= "PART_RowDefinition_Menu";
		RowDefinition rowDefinition_Menu;
		public const string PART_RowDefinition_Content			= "PART_RowDefinition_Content";
		RowDefinition rowDefinition_Content;
		public const string PART_RowDefinition_OutputDisplay	= "PART_RowDefinition_OutputDisplay";
		RowDefinition rowDefinition_OutputDisplay;
		public const string PART_RowDefinition_StatusBar		= "PART_RowDefinition_StatusBar";
		RowDefinition rowDefinition_StatusBar;
		public const string PART_Border_Banner					= "PART_Border_Banner";
		Border border_Banner;
		public const string PART_Expander_Banner				= "PART_Expander_Banner";
		Expander expander_Banner;
		public const string PART_StackPanel_TitleBanner			= "PART_StackPanel_TitleBanner";
		StackPanel stackPanel_TitleBanner;
		public const string PART_ShellBanner = "PART_ShellBanner";
		ShellBanner shellBanner;
		public const string PART_ContentControl_Menu			= "PART_ContentControl_Menu";
		ContentControl contentControl_Menu;
		public const string PART_StackPanel_Menu				= "PART_StackPanel_Menu";
		StackPanel stackPanel_Menu;
		public const string PART_Border_Workspace				= "PART_Border_Workspace";
		Border border_Workspace;
		public const string PART_TabControl_Workspace			= "PART_TabControl_Workspace";
		TabControl tabControl_Workspace;
		public const string PART_Border_Left					= "PART_Border_Left";
		Border border_Left;
		public const string PART_Expander_Left					= "PART_Expander_Left";
		Expander expander_Left;
		public const string PART_TabControl_Left				= "PART_TabControl_Left";
		TabControl tabControl_Left;
		public const string PART_Border_Right					= "PART_Border_Right";
		Border border_Right;
		public const string PART_Expander_Right					= "PART_Expander_Right";
		Expander expander_Right;
		public const string PART_TabControl_Right				= "PART_TabControl_Right";
		TabControl tabControl_Right;
		public const string PART_Border_Bottom					= "PART_Border_Bottom";
		Border border_Bottom;
		public const string PART_Expander_Bottom				= "PART_Expander_Bottom";
		Expander expander_Bottom;
		public const string PART_TabControl_Bottom				= "PART_TabControl_Bottom";
		TabControl tabControl_Bottom;
		public const string PART_StackPanel_StatusBar			= "PART_StackPanel_StatusBar";
		StackPanel stackPanel_StatusBar;
		public const string PART_GridSplitter_Left				= "PART_GridSplitter_Left";
		GridSplitter gridSplitter_Left;
		public const string PART_GridSplitter_Bottom			= "PART_GridSplitter_Bottom";
		GridSplitter gridSplitter_Bottom;
		public const string PART_GridSplitter_Right				= "PART_GridSplitter_Right";
		GridSplitter gridSplitter_Right;

		static ShellView()
		{
			DefaultStyleKeyProperty.OverrideMetadata(
				typeof(ShellView), new FrameworkPropertyMetadata(typeof(ShellView)));
		}

		public ShellView()
		{
			Loaded += OnLoaded;

			AttachCommandBindings();

			Closing += OnWindowClosing;

			DataContext = new ShellViewModel(this);

			WindowStartupLocation = WindowStartupLocation.CenterScreen;

			SetValue(RegionManager.RegionNameProperty, RegionNames.Shell);
		}

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			try
			{
				OnApplyTemplateAux();
			}
			catch (Exception ex)
			{
				Debug.Fail("Problem applying templates to ShellView.");
				Log.Error("Problem applying templates to ShellView.", ex);
				throw;
			}
		}

		void OnApplyTemplateAux()
		{
			grid_Root = (Grid)Template.FindName(PART_Grid_Root, this);
			if (grid_Root != null)
			{
				grid_Root.AddHandler(UIElement.GotFocusEvent, new RoutedEventHandler(Grid_Root_GotFocus));
			}
			columnDefinition_Left = Template.FindName(PART_ColumnDefinition_Left, this) as ColumnDefinition;
			columnDefinition_Center = Template.FindName(PART_ColumnDefinition_Center, this) as ColumnDefinition;
			columnDefinition_Right = Template.FindName(PART_ColumnDefinition_Right, this) as ColumnDefinition;
			rowDefinition_Banner = Template.FindName(PART_RowDefinition_Banner, this) as RowDefinition;
			rowDefinition_Menu = Template.FindName(PART_RowDefinition_Menu, this) as RowDefinition;
			rowDefinition_Content = Template.FindName(PART_RowDefinition_Content, this) as RowDefinition;
			rowDefinition_OutputDisplay = Template.FindName(PART_RowDefinition_OutputDisplay, this) as RowDefinition;
			rowDefinition_StatusBar = Template.FindName(PART_RowDefinition_StatusBar, this) as RowDefinition;
			border_Banner = Template.FindName(PART_Border_Banner, this) as Border;
			expander_Banner = Template.FindName(PART_Expander_Banner, this) as Expander;
			if (expander_Banner != null)
			{
				expander_Banner.Expanded += Expander_Banner_ExpandChanged;
				expander_Banner.Collapsed += Expander_Banner_ExpandChanged;
			}
			stackPanel_TitleBanner = Template.FindName(PART_StackPanel_TitleBanner, this) as StackPanel;
			Template.TryFindNamedElement(PART_ShellBanner, this, out shellBanner);
			contentControl_Menu = Template.FindName(PART_ContentControl_Menu, this) as ContentControl;
			stackPanel_Menu = Template.FindName(PART_StackPanel_Menu, this) as StackPanel;
			border_Workspace = Template.FindName(PART_Border_Workspace, this) as Border;
			tabControl_Workspace = Template.FindName(PART_TabControl_Workspace, this) as TabControl;
			if (tabControl_Workspace != null)
			{
				tabControl_Workspace.SelectionChanged += workspaceTabControl_SelectionChanged;
			}
			border_Left = Template.FindName(PART_Border_Left, this) as Border;
			expander_Left = Template.FindName(PART_Expander_Left, this) as Expander;
			if (expander_Left != null)
			{
				expander_Left.Expanded += Expander_Left_ExpandChanged;
				expander_Left.Collapsed += Expander_Left_ExpandChanged;
			}
			tabControl_Left = Template.FindName(PART_TabControl_Left, this) as TabControl;
			if (tabControl_Left != null)
			{
				tabControl_Left.SelectionChanged += OnTabControl_Left_SelectionChanged;
			}
			border_Right = Template.FindName(PART_Border_Right, this) as Border;
			expander_Right = Template.FindName(PART_Expander_Right, this) as Expander;
			if (expander_Right != null)
			{
				expander_Right.Expanded += Expander_Right_ExpandChanged;
				expander_Right.Collapsed += Expander_Right_ExpandChanged;
			}
			tabControl_Right = Template.FindName(PART_TabControl_Right, this) as TabControl;
			if (tabControl_Right != null)
			{
				tabControl_Right.SelectionChanged += OnTabControl_Right_SelectionChanged;
			}
			border_Bottom = Template.FindName(PART_Border_Bottom, this) as Border;
			expander_Bottom = Template.FindName(PART_Expander_Bottom, this) as Expander;
			if (expander_Bottom != null)
			{
				expander_Bottom.Expanded += Expander_Bottom_ExpandChanged;
				expander_Bottom.Collapsed += Expander_Bottom_ExpandChanged;
			}
			tabControl_Bottom = Template.FindName(PART_TabControl_Bottom, this) as TabControl;
			if (tabControl_Bottom != null)
			{
				tabControl_Bottom.SelectionChanged += OnTabControl_Bottom_SelectionChanged;
			}
			stackPanel_StatusBar = Template.FindName(PART_StackPanel_StatusBar, this) as StackPanel;
			gridSplitter_Left = Template.FindName(PART_GridSplitter_Left, this) as GridSplitter;
			gridSplitter_Bottom = Template.FindName(PART_GridSplitter_Bottom, this) as GridSplitter;
			gridSplitter_Right = Template.FindName(PART_GridSplitter_Right, this) as GridSplitter;

			var menu = Template.FindName("PART_Menu", this) as FrameworkElement;
			if (menu != null)
			{
				menu.BeginInit();
				menu.EndInit();
				menu.ApplyTemplate();
			}

			var toolBar = Template.FindName("PART_ToolBar", this) as FrameworkElement;
			if (toolBar != null)
			{
				toolBar.BeginInit();
				toolBar.EndInit();
				toolBar.ApplyTemplate();
			}
		}

		bool loaded;

		void OnLoaded(object sender, RoutedEventArgs e)
		{
			UpdateAllRegionDimensions();
			if (loaded)
			{
				return;
			}
			loaded = true;

			AttachUIElementStateChangedEvent();

			InitializeDimensionDefaults();
			OnViewLoaded(e);
		}

		void AttachUIElementStateChangedEvent()
		{
			var eventAggregator = ServiceLocatorSingleton.Instance.GetInstance<IEventAggregator>();
			var stateChangedEvent = eventAggregator.GetEvent<UIElementStateChangedEvent>();
			stateChangedEvent.Subscribe(OnUIElementStateChanged);
		}

		ShellViewModel ShellViewModel
		{
			get
			{
				return (ShellViewModel)DataContext;
			}
		}

		public string TitleFormat
		{
			get
			{
				return ShellViewModel.TitleFormat;
			}
			set
			{
				SetTitleFormat(value);
			}
		}

		public bool BannerVisible
		{
			get
			{
				return ShellViewModel.BannerVisible;
			}
			set
			{
				ShellViewModel.BannerVisible = value;
			}
		}

		public bool LogoVisible
		{
			get
			{
				return ShellViewModel.LogoVisible;
			}
			set
			{
				ShellViewModel.LogoVisible = value;
			}
		}

		void SetTitleFormat(string format)
		{
			Binding nameBinding = new Binding("Title")
											{
												Source = DataContext,
												StringFormat = format
											};
			SetBinding(TitleProperty, nameBinding);
			ShellViewModel.TitleFormat = format;
		}

		public IEnumerable<IView> WorkspaceViews
		{
			get
			{
				return GetWorkspaceViews();
			}
		}

		IView lastSelectedTabView;

		void workspaceTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			if (e.OriginalSource != sender)
			{
				return;
			}

			lastSelectedTabView = null;

			var selectedTabView = tabControl_Workspace.SelectedItem as IView;
			lastSelectedTabView = selectedTabView;
			if (selectedTabView == null)
			{
				return;
			}

			/* When the workspace selected item changes
			 * we raise the SelectedWorkspaceViewChangedEvent. */
			PublishActiveWorkspaceViewChanged(selectedTabView);
			//lastActiveView = selectedTabView;

			var content = selectedTabView as UIElement;
			if (content != null)
			{
				content.Focus();
			}
		}

		void PublishActiveWorkspaceViewChanged(IView workspaceView)
		{
			var eventAggregator = ServiceLocatorSingleton.Instance.GetInstance<IEventAggregator>();
			eventAggregator.GetEvent<SelectedWorkspaceViewChangedEvent>().Publish(workspaceView);
			eventAggregator.GetEvent<ActiveViewChangedInShellEvent>().Publish(workspaceView);
		}

		void PublishActiveNonWorkspaceViewChanged(IView view)
		{
			var eventAggregator = ServiceLocatorSingleton.Instance.GetInstance<IEventAggregator>();
			eventAggregator.GetEvent<SelectedWorkspaceViewChangedEvent>().Publish(null);
			eventAggregator.GetEvent<ActiveViewChangedInShellEvent>().Publish(view);
		}

		/// <summary>
		/// Gets the selected content in the shell. First we check the active region
		/// to see if it has content that is of the specified type, then we fall
		/// back on the current active view in the workspace.
		/// </summary>
		/// <returns>The selected content of the type specified, or <code>null</code>.</returns>
		TContent GetSelectedContent<TContent>() where TContent : class
		{
			var content = GetActiveRegionContent<TContent>();
			if (content != null)
			{
				return content;
			}
			var view = MainView;
			if (view == null)
			{
				return default(TContent);
			}
			content = view as TContent ?? view.ViewModel as TContent;
			return content;
		}

		TContent GetActiveRegionContent<TContent>() where TContent : class
		{		
			var activeViews = (from region in RegionManagerPrivate.Regions
							   from view in region.ActiveViews
							   select view).OfType<IView>();
			foreach (var view in activeViews)
			{
				var content = GetContentFromView<TContent>(view);
				if (content != null)
				{
					return content;
				}
			}
			return default(TContent);
		}

		static TContent GetContentFromView<TContent>(IView view) where TContent : class
		{
			var content = view as TContent;
			if (content != null)
			{
				return content;
			}
			content = view.ViewModel as TContent;
			return content;
		}

		object GetActiveRegionContent(Type contentType)
		{
			var activeViews = (from region in RegionManagerPrivate.Regions
							   from view in region.ActiveViews
							   select view).OfType<IView>();
			foreach (var view in activeViews)
			{
				if (contentType.IsAssignableFrom(view.GetType()))
				{
					return view;
				}

				if (view.ViewModel != null && contentType.IsAssignableFrom(view.ViewModel.GetType()))
				{
					return view.ViewModel;
				}
			}
			return null;
		}

		public IView ActiveView
		{
			get
			{
				return GetActiveView();
			}
		}

		IRegion GetRegion(string regionName)
		{
			return RegionManagerPrivate.GetRegion(regionName);
		}

		IRegion GetViewRegion(IView view)
		{
			return RegionManagerPrivate.GetViewRegion(view);
		}

		#region CustomActiveView Dependency Property

		//public static DependencyProperty CustomActiveViewProperty = DependencyProperty.RegisterAttached(
		//    "CustomActiveView", typeof(IView), typeof(ShellView), new FrameworkPropertyMetadata(OnCustomActiveViewChanged));

		//public static void SetCustomActiveView(DependencyObject element, IView value)
		//{
		//    element.SetValue(CustomActiveViewProperty, value);
		//}

		//public static IView GetCustomActiveViewProperty(DependencyObject element)
		//{
		//    return (IView)element.GetValue(CustomActiveViewProperty);
		//}

		//IView customActiveView;

		//public static void OnCustomActiveViewChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
		//{
		//    var shellView = obj as ShellView;
		//    if (shellView == null)
		//    {
		//        return;
		//    }
		//    var newValue = (IView)args.NewValue;
		//    shellView.customActiveView = newValue;
		//}

		#endregion

		IView GetActiveView()
		{
			//var view = customActiveView;
			//if (view != null)
			//{
			//    return view;
			//}

			/* First we look for the view of the focussed element. */
			var focusedElement = FocusManager.GetFocusedElement(this) as UIElement;
			if (focusedElement != null)
			{
				IView activeView = focusedElement.FindAncestorOrSelf<IView>();
				if (activeView != this) /* The shell view is an IView, and we don't want that. */
				{
					/* At this point the focused item may not be the active view. */
					if (tabControl_Workspace != null 
						&& !tabControl_Workspace.Items.Contains(activeView))
					{
						return activeView;
					}
				}
			}

			/* Otherwise we grab the first active view we find, starting with the workspace. */
			var workspaceContent = tabControl_Workspace != null 
				? tabControl_Workspace.SelectedContent as IView : null;
			if (workspaceContent != null)
			{
				return workspaceContent;
			}

			var leftContent = GetViewContent(tabControl_Left);
			if (leftContent != null)
			{
				return leftContent;
			}
			var bottomContent = GetViewContent(tabControl_Bottom);
			if (bottomContent != null)
			{
				return bottomContent;
			}

			var rightContent = GetViewContent(tabControl_Right);
			if (rightContent != null)
			{
				return rightContent;
			}

			return null;
		}

		#region MainView Attached Property

		public static DependencyProperty MainViewProperty = DependencyProperty.RegisterAttached(
			"MainView", typeof(IView), typeof(ShellView), 
			new FrameworkPropertyMetadata(OnMainViewChanged));

		public static void SetMainView(DependencyObject element, IView value)
		{
			element.SetValue(MainViewProperty, value);
		}

		public static IView GetMainView(DependencyObject element)
		{
			return (IView)element.GetValue(MainViewProperty);
		}

		IView mainView;

		public static void OnMainViewChanged(
			DependencyObject obj, DependencyPropertyChangedEventArgs args)
		{
			var element = obj as UIElement;
			if (element == null)
			{
				return;
			}
			element.Focus();
			var shellView = element.FindAncestorOrSelf<ShellView>();
			if (shellView == null)
			{
				return;
			}
			IView newValue = (IView)args.NewValue;
			shellView.mainView = newValue;
			shellView.PublishActiveWorkspaceViewChanged(newValue);
		}

		#endregion

		public IView MainView
		{
			get
			{
				var view = mainView;
				if (view != null)
				{
					return view;
				}
				return tabControl_Workspace != null 
					? tabControl_Workspace.SelectedContent as IView : null;
			}
		}

		IView GetViewContent(TabControl tabControl)
		{
			if (tabControl != null)
			{
				var content = tabControl.SelectedContent as IView;
				if (content != null)
				{
					var view = content as IActiveAware;
					if (view != null && view.IsActive)
					{
						return (IView)view;
					}
				}
			}
			return null;
		}

		void OnWindowClosing(object sender, CancelEventArgs e)
		{
			var allViewsClosed = Close(false);
			if (!allViewsClosed)
			{
				e.Cancel = true;
			}
		}
        
		ISavableContent GetSavableContent()
		{
			var savableContent = GetSelectedContent<ISavableContent>();
			if (savableContent == null)
			{
				var savableContentProvider = GetSelectedContent<IContentProvider<ISavableContent>>();
				if (savableContentProvider != null)
				{
					savableContent = savableContentProvider.Content;
				}
			}
			return savableContent;
		}

		//#region CustomWorkspaceViewsProperty
		//public static DependencyProperty CustomWorkspaceViewsProperty = DependencyProperty.RegisterAttached(
		//    "CustomWorkspaceViews", typeof(IEnumerable<IView>), typeof(ShellView),
		//    new FrameworkPropertyMetadata(OnCustomWorkspaceViewsChanged));

		//public static void SetCustomWorkspaceViews(DependencyObject element, IView value)
		//{
		//    element.SetValue(CustomActiveViewProperty, value);
		//}

		//public static IView GetCustomWorkspaceViews(DependencyObject element)
		//{
		//    return (IView)element.GetValue(CustomWorkspaceViewsProperty);
		//}

		//IEnumerable<IView> customWorkspaceViews;

		//public static void OnCustomWorkspaceViewsChanged(
		//    DependencyObject obj, DependencyPropertyChangedEventArgs args)
		//{
		//    var shellView = obj as ShellView;
		//    if (shellView == null)
		//    {
		//        return;
		//    }
		//    shellView.customWorkspaceViews = (IEnumerable<IView>)args.NewValue;
		//}
		//#endregion

		//#region CustomViewsProperty
		//public static DependencyProperty CustomViewsProperty = DependencyProperty.RegisterAttached(
		//    "CustomViews", typeof(IEnumerable<IView>), typeof(ShellView),
		//    new FrameworkPropertyMetadata(OnCustomViewsChanged));

		//public static void SetCustomViews(DependencyObject element, IView value)
		//{
		//    element.SetValue(CustomActiveViewProperty, value);
		//}

		//public static IView GetCustomViews(DependencyObject element)
		//{
		//    return (IView)element.GetValue(CustomViewsProperty);
		//}

		//IEnumerable<IView> customViews;

		//public static void OnCustomViewsChanged(
		//    DependencyObject obj, DependencyPropertyChangedEventArgs args)
		//{
		//    var shellView = obj as ShellView;
		//    if (shellView == null)
		//    {
		//        return;
		//    }
		//    shellView.customViews = (IEnumerable<IView>)args.NewValue;
		//}
		//#endregion

		IEnumerable<TContent> GetAllContentOfType<TContent>() where TContent : class
		{
			var activeViews = (from region in RegionManagerPrivate.Regions
							   from view in region.ActiveViews
							   select view).OfType<IView>();

			var result = new List<TContent>();
			foreach (var view in activeViews)
			{
				var content = GetContentFromView<TContent>(view);
				if (content != null)
				{
					result.Add(content);
				}
			}

			var workspaceViews = GetWorkspaceViews();
			foreach (var view in workspaceViews)
			{
				var content = GetContentFromView<TContent>(view);
				if (content != null && !result.Contains(content))
				{
					result.Add(content);
				}
			}
			return result;
		}

		IRegionManager RegionManagerPrivate
		{
			get
			{
				var result = (IRegionManager)GetValue(RegionManager.RegionManagerProperty);
				if (result == null)
				{
					result = ServiceLocatorSingleton.Instance.GetInstance<IRegionManager>();
				}
				return result;
			}
		}

		IEnumerable<IView> GetWorkspaceViews()
		{
			if (!RegionManagerPrivate.Regions.ContainsRegionWithName(RegionNames.Workspace))
			{
				/* This can occur if a command evaluation occurs 
				 * before the ShellView has finished applying templates. */
				return new List<IView>();
			}

			var workspaceRegion = RegionManagerPrivate.Regions[RegionNames.Workspace];
			if (workspaceRegion == null)
			{
				Debug.Fail("Workspace region should not be null.");
				return new List<IView>();
			}
			var views = from workspaceView in workspaceRegion.Views 
						select workspaceView;
			return views.OfType<IView>().ToList();
		}

		IEnumerable<ISavableContent> GetAllSavableContent()
		{
			return GetAllContentOfType<ISavableContent>();
		}

		public IViewModel ViewModel
		{
			get
			{
				return (IViewModel)DataContext;
			}
		}

		#region ViewLoaded event

		event EventHandler<EventArgs> viewLoaded;

		public event EventHandler<EventArgs> ViewLoaded
		{
			add
			{
				viewLoaded += value;
			}
			remove
			{
				viewLoaded -= value;
			}
		}

		protected void OnViewLoaded(EventArgs e)
		{
			if (viewLoaded != null)
			{
				viewLoaded(this, e);
			}
		}
		#endregion

		public bool Close(bool force)
		{
			var viewService = ServiceLocatorSingleton.Instance.GetInstance<IViewService>();
			
			foreach (var view in WorkspaceViews)
			{
				if (!viewService.CloseView(view, false))
				{
					return false;
				}
			}
			return true;
		}

		IView lastActiveView;

		void Grid_Root_GotFocus(object sender, RoutedEventArgs e)
		{
			SetActiveView();
		}

		void OnUIElementStateChanged(ViewStateChangedEventArgs e)
		{
			if (e.UIElement == null)
			{
				throw new ArgumentException("UIElement property should not be null");
			}
			var eventAggregator = ServiceLocatorSingleton.Instance.GetInstance<IEventAggregator>();
			var activeView = e.UIElement.FindAncestorOrSelf<IView>();
			if (activeView == null)
			{
				return;
			}

			var temp = lastActiveView;
			lastActiveView = activeView;

			if (activeView == temp)
			{
				switch (e.UIElementState)
				{
					case UIElementState.Unloaded:
					case UIElementState.LostFocus:
						eventAggregator.GetEvent<ActiveViewChangedInShellEvent>().Publish(null);
						break;
					case UIElementState.Loaded:
					case UIElementState.GotFocus:
						eventAggregator.GetEvent<ActiveViewChangedInShellEvent>().Publish(activeView);
						break;
				}
			}
			else
			{
				switch (e.UIElementState)
				{
					case UIElementState.Loaded:
					case UIElementState.GotFocus:
						eventAggregator.GetEvent<ActiveViewChangedInShellEvent>().Publish(activeView);
						break;
				}
			}
			lastActiveView = activeView;
		}

		/// <summary>
		/// Sets the active view to the view containing the focused element.
		/// If no view is found to be a parent of the focused element, 
		/// the the active workspace view is used and focused.
		/// </summary>
		void SetActiveView()
		{
			var focusedElement = FocusManager.GetFocusedElement(this);
			var control = focusedElement as UIElement;

			var contentControl = control as ContentControl;
			if (contentControl != null)
			{
				var temp = contentControl.Content as UIElement;
				if (temp != null)
				{
					control = temp;
				}
			}
			IView activeView = null;
			if (control != null && !(control is IView))
			{
				activeView = control.FindAncestor<IView>();
				if (activeView == this)
				{
					activeView = null;
				}
			}

			var main = MainView;
			if (activeView == null && main != null)
			{
				activeView = main;
				//var focusable = activeView as UIElement;
				//if (focusable != null)
				//{
				//    focusable.Focus();
				//}
			}
			
			if (activeView == lastActiveView)
			{
				return; /* Already signalled. */
			}
			lastActiveView = activeView;

			var eventAggregator = ServiceLocatorSingleton.Instance.GetInstance<IEventAggregator>();
			eventAggregator.GetEvent<ActiveViewChangedInShellEvent>().Publish(activeView);

			CommandManager.InvalidateRequerySuggested();
		}

	}
}
